在上一篇看了IDialogContext的作用以及如何用3個主要作用的state的部分來儲存使用者相關的訊息。
到目前爲止我們的機器人回復的内容都是文字。如果今天我的内容比較豐富,例如有圖片+文字怎麽辦?有沒有更好的呈現方式。
這篇將來看看Activity裡面的Attachment搭配Card呈現多元樣式的概念。
還記得我們之前提到,所有的訊息流通其實都是一個IActivity,其實從chatbot發出去的也是IActivity,只不過是一個更訊息像的IMessageActivity(如果看看 IDialogContext.PostAsync會發現除了允許一個字串之外,也允許接受一個IMessageActivity)。
而Attachment是IMessageActivity的其中一個欄位,可以放不同類型的内容。
在這一篇,將會著重放一些豐富格式的内容。由於這些豐富格式看起來像一張一張卡片,因此又稱之爲Rich Card Message
Animation Card
可以播放影片或者GIF檔案的卡片
Audio Card
可以播放聲音檔案的卡片
Hero Card
有一張大圖,一些文字説明,然後一些按鈕點下去可以觸發別的事情。Hero Card是用的最多的一種
Thumbnail Card
呈現圖片的卡片
Receipt Card
收據卡。可以有品相,然後最後的費用
Sigin Card
讓bot登入到第三方系統的卡片
Video Card
可以用用來播放影片的卡片
以上是所有内建的卡片,雖然大部分的情景都夠使用了,但是如果有些想要自己定義怎麽辦?
BotBuilder有支援Adaptive Card,因此可以自己定義不同組合。
Adaptive Card不是BotBuilder才有的東西,這個是微軟提出的一個概念,BotBuilder只是剛好其中一個有支援這個樣式的框架之一。詳細資訊可以參考:https://adaptivecards.io/
有些卡片樣式可以定義一些按鈕可以讓使用者點選,這些動作會是CardAction形態。
有了上面的概念之後,我們來實際實作一下這些不同的卡片樣式,我們不會全部都做,但是至少給大家一個feel怎麽建立出這些卡片,然後最後會留給大家一個參考鏈接。
我們會實作:
Hero Card
Receipt Card
Adaptive Card
回到我們的情景,使用者可能會查某一家飯店,那這個時候我們要返回這家飯店的圖片,以及讓使用者點了可以到官網的按鈕。
要達成這個目標,首先我們先增加一個新的判斷:
...
if (activity.Text == "查飯店")
{
// 返回飯店的圖片以及可以打開官網的按鈕
}
else
{
// 已經有姓名直接輸出 姓名 + 輸入内容
await context.PostAsync($"{name}: {activity.Text}");
}
...
上面的code還是延續我們之前的sample程式碼,只是本來echo使用者輸入的内容,現在變成特定文字會做特定事情,其餘保留。
再來是建立出一個要被回復出去的IMessageActivity,并且組出我們要的HeroCard,最後把結果輸出
// 建立一個回復
var returnMessage = activity.CreateReply();
// 建立一個HeroCard
var herocard = new HeroCard()
{
Title = "xxx飯店",
Text = "5星級高級大飯店",
Images = new List<CardImage>()
{
new CardImage("https://cdn.pixabay.com/photo/2016/02/10/13/32/hotel-1191709_1280.jpg")
},
Buttons = new List<CardAction>()
{
new CardAction("openUrl", "官網", "http://www.google.com")
}
};
returnMessage.Attachments =new List<Attachment>() { herocard.ToAttachment() };
// 送出
await context.PostAsync(returnMessage);
測試結果
從上面的測試結果,可以看出如果我們整個串起來,從詢問名字到請問使用者用途,整體起來開始有點feel了。
然後我們的HeroCard也讓他看起來便的更加漂亮,除了有圖片,還可以點下去開啓官網
題外話,CardAction還可以有不同類型,我們目前這種是開啓網頁,其他還有例如點了就輸入某些内容方便輸入。
假設今天使用者想要訂房,我們可能會顯示會需要多少錢,這個時候就可以使用Receipt Card。
else if(activity.Text == "訂房")
{
var returnMessage = activity.CreateReply();
var receiptCard = new ReceiptCard()
{
Title = "訂房費用",
Total = "NT$ 120",
Tax = "NT$ 20",
Items = new List<ReceiptItem>()
{
new ReceiptItem()
{
Title = "1大房",
Price = "90",
Quantity = "1",
Image = new CardImage("https://cdn.pixabay.com/photo/2014/08/11/21/40/wall-416062__180.jpg")
},
new ReceiptItem()
{
Title = "飲料",
Price = "10",
Quantity = "1",
Image = new CardImage("https://cdn.pixabay.com/photo/2014/09/26/19/51/coca-cola-462776_1280.jpg")
}
}
};
returnMessage.Attachments = new List<Attachment>() { receiptCard.ToAttachment() };
await context.PostAsync(returnMessage);
}
上面的程式碼其實和HeroCard沒什麽兩樣,差異只在於他們自己有的特定欄位,但是光看名稱應該就蠻好理解。
執行結果
内建的一些卡片樣式其實很夠大部分使用情景了。但是,如果今天你想要自己刻畫呈現的内容怎麽辦?
例如說我希望,查飯店的時候,左邊是飯店的一些資訊,右邊則是飯店圖片的縮圖,透過内建的卡片樣式是做不到。這個時候就需要Adaptive Card。
要使用Adaptive Card,先安裝的套件,直接使用nuget:Install-Package Microsoft.AdaptiveCards
今天我想達到的效果是,左邊是飯店資訊,右邊是飯店的圖片,然後有鏈接可以點去官網,在Adaptive Card我們可以透過使用ColumnSet做到
以下是完整的程式碼:
...
else if(activity.Text == "查飯店v2")
{
var returnMessage = activity.CreateReply();
var card = new AdaptiveCard();
var columSet = new ColumnSet();
columSet.Columns.Add(new Column()
{
Size = "1",
Items = new List<CardElement>()
{
new TextBlock()
{
Text = "豪華大飯店",
Weight = TextWeight.Bolder,
Size = TextSize.ExtraLarge
},
new TextBlock()
{
Text = "4.2 ★★★☆ (120) ",
IsSubtle = true,
Wrap = false
}
}
});
columSet.Columns.Add(new Column()
{
Size = "1",
Items = new List<CardElement>()
{
new Image()
{
Url = "https://cdn.pixabay.com/photo/2016/02/10/13/32/hotel-1191709_1280.jpg",
Size = ImageSize.Auto
}
}
});
card.Body.Add(columSet);
card.Actions.Add(new OpenUrlAction()
{
Title = "官網",
Url = "http://wwww.google.com"
});
returnMessage.Attachments.Add(new Attachment()
{
Content = card,
ContentType = AdaptiveCard.ContentType
});
await context.PostAsync(returnMessage);
}
...
上面的程式碼相較於之前的長很多,但是如果仔細看會發現很多都是在設定排版相關的資訊。如果直接和下圖的結果對照應該就能夠看出感覺:
adaptive card輸出的結果
如果對Adaptive Card有興趣,可以去他的官網(官網介紹)瞭解更多,他那裏有一個綫上UI設計的工具(Visualizer),可以那邊設計好了再來寫這個code。 然後這個是微軟另外一個project,因此別的地方也可以使用
不過Adaptive Card在某些channel上面是不支援的,後面我們在提到channel支援的部分會在提到,大家先有個印象就好。
透過這篇是不是開始覺得chatbot也是可以很漂亮的。輸出内容不再是醜醜的文字,而是做成類似卡片的方式輸出,如果内建格式不夠使用,還可以使用Adaptive Card來自己微調。
到目前爲止大家是不是開始覺得程式碼有點髒了?所有邏輯都寫在一起,然後根本是靠if else來切換邏輯。那如果需要使用者輸入類似表單多欄位不是要寫到死?
下一篇,我們先對於表單欄位輸入類型的處理方式透過FormFlow介紹一下,然後關於邏輯切割的部分在之後的Dialogs章節在介紹。